
#include "ompt-internal.h"
#include "ompt-specific.h"
#include "libgomp.h"
#include <assert.h>

inline struct gomp_thread* ompt_get_thread()
{
  return gomp_thread();
}


struct gomp_team *ompt_team(int ancestor_level)
{
  int i;
  struct gomp_thread *th = ompt_get_thread();
  struct gomp_team *team = th ? th->ts.team : NULL;

  for (i = 0; team && (i < ancestor_level); i++) {
    team = team->prev_ts.team;
  }
  return team;
} 


/* safely extract a task from a thread. 
 * - a thread need not be an openmp thread
 * - an openmp thread may have an uninitialized task
 */
struct gomp_task *ompt_task(int ancestor_level)
{
  int i;
  struct gomp_thread *th = ompt_get_thread();
  struct gomp_task *task = th ? th->task : NULL;

  for (i = 0; task && (i < ancestor_level); i++) {
    task = task->parent;
  }
  return task;
} 


ompt_state_t __ompt_get_state_internal(ompt_wait_id_t *ompt_wait_id)
{
  struct gomp_thread  *ti = ompt_get_thread();

  if (ti) {
    *ompt_wait_id = ti->ompt_thread_info.wait_id;
    return ti->ompt_thread_info.state;
  } else {
    return ompt_state_undefined;
  }
}



void *__ompt_get_idle_frame_internal(void)
{
  struct gomp_thread  *ti = ompt_get_thread();

  return ti ? ti->ompt_thread_info.idle_frame : NULL;
}


ompt_lw_taskteam_t *__ompt_get_lw_taskteam(int *ancestor_level)
{
  int level = *ancestor_level;
  struct gomp_thread  *ti = ompt_get_thread();
  if (ti) {
    ompt_lw_taskteam_t *lwt = ti->ompt_thread_info.lw_taskteam;
    while (lwt) {
      if (level == 0) return lwt;
      lwt = lwt->parent;
      level--;
    }
    *ancestor_level = level;
  } 
  return NULL;
}


void *__ompt_get_parallel_function_internal(int ancestor_level) 
{
  int level = ancestor_level;
  ompt_lw_taskteam_t *lwt = __ompt_get_lw_taskteam(&level);
  if (lwt) {
    return lwt->ompt_team_info.microtask;
  } else {
    struct gomp_team *team = ompt_team(level); /* remaining levels */

    //Anitha: FIXME
//    return (void *) (team ? team->t_pkfn : NULL);
     return (void *) (team ? NULL: NULL);
  }
}


ompt_parallel_id_t __ompt_get_parallel_id_internal(int ancestor_level) 
{
  int level = ancestor_level;
  ompt_lw_taskteam_t *lwt = __ompt_get_lw_taskteam(&level);
  if (lwt) {
    return lwt->ompt_team_info.parallel_id;
  } else {
    struct gomp_team *team = ompt_team(level); /* remaining levels */
    return team ? team->ompt_team_info.parallel_id : ompt_parallel_id_none; 
  }
}


ompt_thread_id_t __ompt_get_thread_id_internal() 
{
  assert(0);
  return 0;
}


ompt_task_id_t __ompt_get_task_id_internal(int ancestor_level) 
{
  int level = ancestor_level;
  ompt_lw_taskteam_t *lwt = __ompt_get_lw_taskteam(&level);
  if (lwt) {
    return lwt->ompt_task_info.task_id;
  } else {
    struct gomp_task *task = ompt_task(level); /* remaining levels */
    return task ? task->ompt_task_info.task_id : ompt_task_id_none;
  }
}


void *__ompt_get_task_function_internal(int ancestor_level) 
{
  struct gomp_task *task = ompt_task(ancestor_level);
  return (void *) (task ? task->fn : NULL);
}


ompt_frame_t *__ompt_get_task_frame_internal(int ancestor_level) 
{
  struct gomp_task *task = ompt_task(ancestor_level);

  return task ? &task->ompt_task_info.frame : NULL;
}



#define OMPT_THREAD_ID_BITS 16

// 2013 08 24 - John Mellor-Crummey
//   ideally, a thread should assign its own ids based on thread private data. however, the way the intel 
//   runtime reinitializes thread data structures when it creates teams makes it difficult to maintain 
//   persistent thread data. using a shared variable instead is simple. I leave it to intel to sort 
//   out how to implement a higher performance version in their runtime.  

// when using fetch_and_add to generate the IDs, there isn't any reason to waste bits for thread id.
#if 0
#define NEXT_ID(id_ptr,tid) ((__sync_fetch_and_add(id_ptr, 1) << OMPT_THREAD_ID_BITS) | (tid))
#else
#define NEXT_ID(id_ptr,tid) (__sync_fetch_and_add(id_ptr, 1)) 
#endif

ompt_parallel_id_t __ompt_thread_id_new()
{
  static uint64_t ompt_thread_id = 1;
  return NEXT_ID(&ompt_thread_id, 0);
}

static uint64_t ompt_parallel_id = 1;
ompt_parallel_id_t __ompt_parallel_id_new(int gtid)
{
  return gtid >= 0 ? NEXT_ID(&ompt_parallel_id, gtid) : 0;
}

ompt_parallel_id_t __get_ompt_parallel_id()
{
  return ompt_parallel_id;
}

ompt_task_id_t __ompt_task_id_new(int gtid)
{
  static uint64_t ompt_task_id = 1;
  return NEXT_ID(&ompt_task_id, gtid);
}


void __ompt_team_assign_id(struct gomp_team *team, ompt_parallel_id_t ompt_pid)
{
  team->ompt_team_info.parallel_id = ompt_pid;
}


void __ompt_thread_assign_wait_id(void *variable)
{
  struct gomp_thread *ti = ompt_get_thread();

#ifdef __LP64__
  ti->ompt_thread_info.wait_id = (ompt_wait_id_t)(variable);
#else
  // the ompt_wait_id_t is specified to be uint64_t. Therefore, we cast this twice.
  ti->ompt_thread_info.wait_id = (ompt_wait_id_t)((uint32_t)(variable));
#endif
}


const char *__ompt_get_runtime_version_internal()
{
  return "1.1";
}

